<?php
declare(strict_types=1);

namespace Zms\Validate\Rule;

use Zms\Unit\Is;

/**
 * @desc 银行卡校验
 * @author 竹林风 zms-pro@qq.com  2025-11-24
 */
class BankCardValidate extends RuleAbstract
{

    /**
     * @desc 是否严格模式
     *        false  通用验证  4242 4242 4242 4242 424 或 4242-4242-4242-4242-424
     *        false  严格验证  4242424242424242424
     * @var bool
     * @author 竹林风 zms-pro@qq.com  2025-11-24
     */
    protected $_strict = false;


    /**
     * 是否开启luhn算法校验
     * @var bool
     */
    protected $_luhn = true;


    /**
     * 是否统一把数据转为纯数字类型
     * @var bool
     */
    protected $_convert = true;


    /**
     * 是否开启严格模式
     * @param bool $strict
     * @return $this
     */
    public function setStrict(bool $strict): BankCardValidate
    {
        $this->_strict = $strict;
        return $this;
    }


    /**
     * 是否开启luhn验证规则
     * @param bool $luhn
     * @return $this
     */
    public function setLuhn(bool $luhn): BankCardValidate
    {
        $this->_luhn = $luhn;
        return $this;
    }


    /**
     * 是否转换数据
     * @param bool $convert
     * @return $this
     */
    public function setConvert(bool $convert): BankCardValidate
    {
        $this->_convert = $convert;
        return $this;
    }

    public function validate($value, string $label = ''): bool
    {
        // 如果允许跳过空值且值为空，则直接返回true
        if (Is::empty($value) && $this->_noSkipping === true) {
            $this->_value = $value;
            return true;
        }

        $card = (string)$value;

        //移除所有非数字字符
        if ($this->_strict === false) {
            $card = preg_replace('/[\s\-]/', '', $card);
        }
        //基本检查：长度通常为13~19位，且全为数字
        if (strlen($card) < 13 || strlen($card) > 19) {
            return $this->addError($this->_error, $label);
        }

        //有非数字则验证失败
        if (!preg_match('/^\d+$/', $card)) {
            return $this->addError($this->_error, $label);
        }

        //开头为0视为非法
        if (mb_substr($card, 0, 1) === '0') {
            return $this->addError($this->_error, $label);
        }

        //规则验证
        if ($this->_luhn === false) {
            $this->_value = $this->_convert ? $card : $value;
            return true;
        }
        //规则验证
        if ($this->luhn($value)) {
            $this->_value = $this->_convert ? $card : $value;
            return true;
        }
        return $this->addError($this->_error, $label);
    }


    /**
     * @desc 通过银行卡luhn算法规则
     * @param string $card
     * @return bool
     * @author 竹林风 zms-pro@qq.com  2025-11-24
     */
    protected function luhn(string $card): bool
    {
        $len = strlen($card);
        $all = [];
        $sum_odd = 0;
        $sum_even = 0;
        for ($i = 0; $i < $len; $i++) {
            $all[] = substr($card, $len - $i - 1, 1);
        }
        for ($k = 0; $k < $len; $k++) {
            if ($k % 2 == 0) {
                $sum_odd += $all[$k];
            } else {
                //奇数key都是要相加的偶数和
                if ($all[$k] * 2 >= 10) {
                    $sum_even += $all[$k] * 2 - 9;
                } else {
                    $sum_even += $all[$k] * 2;
                }
            }
        }
        $total = $sum_odd + $sum_even;
        if ($total % 10 == 0) {
            return true;
        }
        return false;
    }
}